//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<dep_stages.h>
///	@path	~/src/lib/utilities/
///	@date	2007/10/16
///	@desc	Dependency stages.

#pragma once

#include "xeres.h"

#include "lib/utilities/debug.h"
#include "lib/utilities/string.h"
#include "lib/utilities/dep_map.h"
#include "lib/utilities/dir_tree.h"

#define	DEPSTAGE_DEBUG		0

namespace xeres {

	enum DepStagesFilterAction
	{
		DEP_STAGES_ACTION_NEXT_NODE = 0 ,	// iterate next node
		DEP_STAGES_ACTION_NEXT_STAGE ,		// skip the rest node and iterate next parent stage
		DEP_STAGES_ACTION_BREAK ,			// break iteration & return
		DEP_STAGES_ACTION_ERROR ,			// error break
		DEP_STAGES_ACTION_last
	};

	/*!
		\class	dep_stages
		\brief	A mixed stage class.
				With directory like stage categories,
				And subsets can use dependency to determine traversal order.
	*/
	template< typename _Content > class dep_stages
	{
	public:

		/// \ctor
		dep_stages( void )
		{
		}

		/// \dtor
		~dep_stages( void )
		{
		}

		/*!
			\brief		Insert new sub-stage.
		*/
		bool insert_stage( const WString& stage , const WString& dep )
		{
			// check
			if( stage.empty() )
			{
				TRACE_ERROR( _S("dep_stages::insert_stage: Input stage name is empty.") );
				return false;
			}

			size_t pos = stage.find_last_of( '/' );

			if( pos != WString::npos )
			{
				WString path = stage.substr( 0 , pos );

				if( m_stages.open_node( path ) == NULL )
				{
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' used undefined path '%s'.") ,
						stage.c_str() , path.c_str() );
					return false;
				}
			}

			// create exist stage
			_Node * node = NULL , * parent = NULL;
			if( m_stages.open_node_with_parent( stage , node , parent ) )
			{
				if( node->m_nodeIsStage )
				{
					// defined as stage directory
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' is defined.") , stage.c_str() );
					return false;
				}
				else
				{
					// exist content
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' is defined as node.") , stage.c_str() );
					return false;
				}
			}

			node = m_stages.open_node( stage , true );
			node->m_dep = dep;
			node->m_nodeIsStage = true;
			node->m_nodeIsPolluted = false;

			// set parent is polluted
			if( parent )
				parent->m_nodeIsPolluted = true;

			return true;
		}

		/*!
			\brief		Insert node into stages, and define dependency (only in the same stage).
		*/
		bool insert_node( const WString& stage , const WString& dep , _Content content )
		{
			// check
			if( stage.empty() )
			{
				TRACE_ERROR( _S("dep_stages::insert_node: Input stage name is empty.") );
				return false;
			}

			size_t pos = stage.find_last_of( '/' );

			if( pos != WString::npos )
			{
				WString path = stage.substr( 0 , pos );

				if( m_stages.open_node( path ) == NULL )
				{
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' used undefined path '%s'.") ,
						stage.c_str() , path.c_str() );
					return false;
				}
			}

			// check exist stage
			_Node * node = NULL , * parent = NULL;
			if( m_stages.open_node_with_parent( stage , node , parent ) )
			{
				if( node->m_nodeIsStage )
				{
					// defined as stage directory
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' is defined as category.") , stage.c_str() );
					return false;
				}
				else
				{
					// exist content
					TRACE_ERROR( _S("dep_stages::insert_node: Stage '%s' content is defined.") , stage.c_str() );
					return false;
				}
			}

			node = m_stages.open_node( stage , true );
			node->m_dep = dep;
			node->m_nodeIsStage = false;
			node->m_nodeIsPolluted = false;
			node->m_content = content;

			// set parent is polluted
			if( parent )
				parent->m_nodeIsPolluted = true;

			return true;
		}

		/*!
			\brief		Replace specific node's content.
		*/
		bool replace_node( const WString& stage , _Content content )
		{
			// check exist stage
			_Node * node = NULL , * parent = NULL;
			if( m_stages.open_node_with_parent( stage , node , parent ) )
			{
				if( node->m_nodeIsStage )
				{
					TRACE_ERROR( _S("dep_stages::replace_node: Stage '%s' is defined as category.") , stage.c_str() );
					return false;
				}
				node->m_content = content;
				return true;
			}
			return false;
		}

		/*!
			\brief		Delete specific node.
		*/
		bool del_node( const WString& stage )
		{
			// check exist stage
			_Node * node = NULL , * parent = NULL;
			if( m_stages.open_node_with_parent( stage , node , parent ) )
			{
				bool result = m_stages.del_node( stage );
				
				// delete node call back to parent
				return result;
			}
			return false;
		}

		/*!
			\brief		Delete the whole stage.
		*/
		bool del_stage( const WString& stage )
		{
			// check exist stage
			_Node * node = NULL , * parent = NULL;
			if( m_stages.open_node_with_parent( stage , node , parent ) )
			{
				bool result = m_stages.del_dir( stage );

				// delete node call back to parent
				return result;
			}
			return false;
		}

		/*!
			\brief		Delete all stages & nodes.
		*/
		void clear( void )
		{
			m_stages.clear();
		}

		/*!
			\class		null_filter
			\brief		Sample class to indicate how to traversal stages.
		*/
		class null_filter
		{
		public:

			/*!
				\brief		Operator to execute while meeting node.
			*/
			DepStagesFilterAction operator () ( const WString& path , const WString& name , _Content& content )
			{
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

			/*!
				\brief		Operator to execute while meeting stage.
			*/
			DepStagesFilterAction operator () ( const WString& currentStage , int depth )
			{
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

			/*!
				\brief		Operator to execute to indicate progress.
			*/
			void operator () ( float progress )
			{
			}
		};

		/*!
			\brief		Traversal stages & nodes.
		*/
		template< typename _NodeFunctor , typename _StageFunctor , typename _ProgressFunctor >
		bool traversal( _NodeFunctor node , _StageFunctor stage , _ProgressFunctor progress )
		{
			TRACK_FUNCTION_SCOPE();

			// check stages
			if( m_stages.empty() )
				return true;

			// directory
#if DEPSTAGE_DEBUG
			TRACE_DEBUG( _S("dep_stages::traversal: Traversal root.") );
#endif // DEPSTAGE_DEBUG

			// for all sub stages
			std::vector< dir_tree< _Node >::iterator > iters;
			m_stages.list_root( iters );

			// build dependency
			dep_map< size_t > dep;
			for( size_t i = 0 ; i < iters.size() ; ++i )
			{
				dep.add_node( i , iters[i].name() , (*iters[i]).m_dep );
			}

			size_t total = iters.size() , current = 0;
			float split = 1.f / (float)total;

			// process context
			_ProcessContext<_NodeFunctor , _StageFunctor , _ProgressFunctor> pc( node , stage , progress );

			// for all sub stages
			dep_map< size_t >::iterator it = dep.begin();
			while( it != dep.end() )
			{
				pc.m_progress = (float)current / (float) total;
				pc.m_quota = split;

				switch( process_stage< _NodeFunctor , _StageFunctor , _ProgressFunctor >( pc , iters[*it] ) )
				{
				case DEP_STAGES_ACTION_NEXT_NODE:
					break;
				case DEP_STAGES_ACTION_NEXT_STAGE:
					return true;
				case DEP_STAGES_ACTION_BREAK:
					return true;
				case DEP_STAGES_ACTION_ERROR:
					return false;
				}
				++it;
				++current;
			}

			return true;
		}

	private:

		// process context
		template< typename _NodeFunctor , typename _StageFunctor , typename _ProgressFunctor >
		struct _ProcessContext
		{
			/// \ctor
			_ProcessContext( _NodeFunctor node , _StageFunctor stage , _ProgressFunctor progress )
				: m_nodeFunctor( node ) , m_stageFunctor( stage ) , m_progressFunctor( progress )
			{

			}

			// functors
			_NodeFunctor			m_nodeFunctor;
			_StageFunctor			m_stageFunctor;
			_ProgressFunctor		m_progressFunctor;

			// execution progress
			float					m_progress;

			// current quota
			float					m_quota;

			// node stack
			StringSeq				m_hierarchy;
		};

		template< typename _Content , typename _NodeFunctor , typename _StageFunctor , typename _ProgressFunctor >
		struct Protector
		{
			/// \ctor
			Protector(
				typename dep_stages< _Content >::_ProcessContext<_NodeFunctor , _StageFunctor , _ProgressFunctor>& pc ,
				const WString& entry )
				: m_pc( pc )
				, m_progress( pc.m_progress )
				, m_quota( pc.m_quota )
			{
				m_pc.m_hierarchy.push_back( entry );
			}
			/// \dtor
			~Protector( void )
			{
				m_pc.m_hierarchy.pop_back();
				m_pc.m_progress = m_progress + m_quota;
				m_pc.m_quota = 0;
			}

			// data
			typename dep_stages< _Content >::_ProcessContext<_NodeFunctor , _StageFunctor , _ProgressFunctor>& m_pc;
			float m_progress;
			float m_quota;
		};

		// nodes
		struct _Node
		{
			// tags
			bool				m_nodeIsStage;

			// need re-calculation traversal
			bool				m_nodeIsPolluted;

			// spare
			bool				m_spare0[2];

			// callback
			_Content			m_content;

			// stage dependency
			WString				m_dep;

		};

		// directory tree
		dir_tree< _Node >	m_stages;

		// process stage
		template< typename _NodeFunctor , typename _StageFunctor , typename _ProgressFunctor >
		DepStagesFilterAction process_stage( _ProcessContext< _NodeFunctor , _StageFunctor , _ProgressFunctor >& pc ,
			typename dir_tree< _Node >::iterator it )
		{
			TRACK_FUNCTION_SCOPE();

			// determine if stage is directory or node
			if( (*it).m_nodeIsStage == false )
			{

#if DEPSTAGE_DEBUG
				TRACE_DEBUG( _S("dep_stages::ProcessStage: Execute stage '%s'.") , it.name().c_str() );
#endif // DEPSTAGE_DEBUG

				// progress
				pc.m_progress += pc.m_quota;
				pc.m_progressFunctor( pc.m_progress );

				// traversal
				WString path;
				for( size_t i = 0 ; i < pc.m_hierarchy.size() ; ++i )
				{
					path += pc.m_hierarchy[i];
					path.push_back( '/' );
				}
				return pc.m_nodeFunctor( path , it.name() , (*it).m_content );
			}
			else
			{
				Protector< _Content , _NodeFunctor , _StageFunctor , _ProgressFunctor > s_protector( pc , it.name() );

#if DEPSTAGE_DEBUG
				TRACE_DEBUG( _S("dep_stages::process_stage: Traversal stage '%s'.") , it.name().c_str() );
#endif // DEPSTAGE_DEBUG

				// for all sub stages
				std::vector< dir_tree< _Node >::iterator > iters;
				if( ! m_stages.list_dir( it , iters ) )
					return DEP_STAGES_ACTION_ERROR;

				if( iters.size() == 0 )
				{
					return DEP_STAGES_ACTION_NEXT_NODE;
				}

				switch( pc.m_stageFunctor( it.name() , it.depth() ) )
				{
				case DEP_STAGES_ACTION_NEXT_NODE:
					break;
				case DEP_STAGES_ACTION_NEXT_STAGE:
					return DEP_STAGES_ACTION_NEXT_NODE;
				case DEP_STAGES_ACTION_BREAK:
					return DEP_STAGES_ACTION_BREAK;
				case DEP_STAGES_ACTION_ERROR:
					return DEP_STAGES_ACTION_ERROR;
				}
				pc.m_progressFunctor( pc.m_progress );

				size_t total = iters.size() , current = 0;
				float split = 1.f / (float)total * pc.m_quota;

				// build dependency
				dep_map< size_t > dep;
				for( size_t i = 0 ; i < iters.size() ; ++i )
				{
					dep.add_node( i , iters[i].name() , (*iters[i]).m_dep );
				}

				// for all sub stages
				dep_map< size_t >::iterator its = dep.begin();
				while( its != dep.end() )
				{
					pc.m_quota = split;

					switch( process_stage< _NodeFunctor , _StageFunctor , _ProgressFunctor >( pc , iters[*its] ) )
					{
					case DEP_STAGES_ACTION_NEXT_NODE:
						break;
					case DEP_STAGES_ACTION_NEXT_STAGE:
						return DEP_STAGES_ACTION_NEXT_NODE;
					case DEP_STAGES_ACTION_BREAK:
						return DEP_STAGES_ACTION_BREAK;
					case DEP_STAGES_ACTION_ERROR:
						return DEP_STAGES_ACTION_ERROR;
					}
					++its;
					++current;
				}
				return DEP_STAGES_ACTION_NEXT_NODE;
			}
		}
 	};

} // namespace xeres
